﻿using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using SoulDoorEngine.Framework;
using SoulDoorEngine.Components.Cameras;
using SoulDoorEngine.Framework.Components;
using SoulDoorEngine.Framework.Screens;

namespace SoulDoorEngine.Components.Scene
{
    public class MaterialManager
    {
        List<Material> materials = new List<Material>();

        public MaterialManager()
        {
            if (!Engine.Services.ContainService(typeof(MaterialManager)))
                Engine.Services.AddService(typeof(MaterialManager), this);

            TexturedMaterial material = new TexturedMaterial();
            material.Name = "Default Material";
            materials.Add(material);
        }

        public void RegisterMaterial(Material Material)
        {
            if (!materials.Contains(Material))
                materials.Add(Material);
        }

        public Material GetMaterial(string Name)
        {
            foreach (Material material in materials)
                if (material.Name == Name)
                    return material;

            return null;
        }

        public void DeleteMaterial(string Name)
        {
            foreach (Material material in materials)
                if (material.Name == Name)
                {
                    materials.Remove(material);
                    break;
                }
        }
    }

    // Base Material class keeps track of an effect and handles basic
    // properties for it
    public class Material : Component
    {
        // The effect to handle
        Effect effect = new BasicEffect(Engine.GraphicsDevice);
        string effectName = "XNA.BasicEffect";

        public virtual Effect Effect
        {
            get { return effect; }
        }

        public virtual string EffectName
        {
            get { return effectName; }
            set
            {
                effectName = value;

                if (effectName == "XNA.BasicEffect")
                {
                    this.effect = new BasicEffect(Engine.GraphicsDevice);
                    ((BasicEffect)effect).TextureEnabled = true;
                    ((BasicEffect)effect).EnableDefaultLighting();
                }
                else
                    this.effect = Engine.Content.Load<Effect>(value);
            }
        }

        public Material(Screen parent)
            : base(parent)
        {
            if (!Engine.Services.ContainService(typeof(MaterialManager)))
                new MaterialManager();

            Engine.Services.GetService<MaterialManager>().RegisterMaterial(this);

            ((BasicEffect)effect).TextureEnabled = true;
            ((BasicEffect)effect).EnableDefaultLighting();
        }

        // Set World, View, and Projection values
        public virtual void Prepare3DDraw(Matrix World)
        {
            ((BasicEffect)effect).World = World;
            ((BasicEffect)effect).View = Engine.Services.GetService<Camera>().View;
            ((BasicEffect)effect).Projection = Engine.Services.GetService<Camera>().Projection;
        }

        public override void SaveSerializationData()
        {
        }

        public override void LoadFromSerializationData()
        {
        }

        public override void Disable()
        {
            Engine.Services.GetService<MaterialManager>().DeleteMaterial(this.Name);
            base.Disable();
        }
    }

    // Material that handles a texture
    public class TexturedMaterial : Material
    {
        // The texture to keep track of
        Texture2D texture;
        string textureName;

        // Set accesor sets the texture to the underlying effect
        public virtual Texture2D Texture
        {
            get { return texture; }
        }

        public virtual string TextureName
        {
            get { return textureName; }
            set
            {
                textureName = value;
                texture = Engine.Content.Load<Texture2D>(value);
            }
        }

        public TexturedMaterial()
            : base(Engine.DefaultScreen)
        {
        }

        public TexturedMaterial(Screen parent)
            : base(parent)
        {
        }

        // Method to set the texture to various types of effect
        internal static void SetTextureToEffect(Effect Effect, Texture2D Texture)
        {
            if (Effect == null)
                return;

            if (Effect is BasicEffect)
                ((BasicEffect)Effect).Texture = Texture;
            else if (Effect.Parameters["Texture"] != null)
                Effect.Parameters["Texture"].SetValue(Texture);
        }

        public override void Prepare3DDraw(Matrix World)
        {
            base.Prepare3DDraw(World);

            SetTextureToEffect(Effect, texture);
        }

        public override void SaveSerializationData()
        {
        }

        public override void LoadFromSerializationData()
        {
        }
    }
}
